import { LRUMap } from "lru_map";
import DataViewRW from "../../utils/DataViewRW";

var formatV1 = 'esriMapCacheStorageModeCompact';
var formatV2 = 'esriMapCacheStorageModeCompactV2';

export default class ParseBundleTile {
    private cache: LRUMap<string, {indexBuffer: ArrayBuffer, budnleBuffer: ArrayBuffer}>;
    private root;
    private options;

    constructor(root, options) {
        this.root = root;
        options = options || {};
        options.packSize = options.packSize || 128;
        if (!options.storageFormat) {
            options.storageFormat = formatV1;
        }
        this.options = options;

        this.cache = new LRUMap(24);
    }

    private outerCallback;
    public getTile(x, y, z, callback) {
        this.outerCallback = callback;
        try {
            let packSize = this.options.packSize;
            let format = this.options.storageFormat;
            let rowSize = (packSize * parseInt((y / packSize).toString())).toString();
            let rGroup = parseInt(rowSize);
            let colSize = (packSize * parseInt((x / packSize).toString())).toString();
            let cGroup = parseInt(colSize);
    
            var bundleBase = this.getBundlePath(this.root, z, rGroup, cGroup);
            if (!this.cache.has(bundleBase)) {//当前需要的bundle文件没有被加载
                let self = this;
                this.cache.set(bundleBase, {budnleBuffer: null, indexBuffer: null});
                this.loadBundleTile(bundleBase, function () { //加载文件
                    self.parseTile(bundleBase, format, packSize, x, cGroup, y, rGroup, callback);
                });
            } else {
                let isLackBundle = (this.cache.get(bundleBase).budnleBuffer == null);
                let isLackIndex = (this.cache.get(bundleBase).indexBuffer == null);
                if (isLackBundle || isLackIndex) {//该bundle文件还没有加载完毕
                    let self = this;
                    setTimeout(() => {
                        self.parseTile(bundleBase, format, packSize, x, cGroup, y, rGroup, callback);
                    }, 1000);
                } else {
                    this.parseTile(bundleBase, format, packSize, x, cGroup, y, rGroup, callback);
                }
            }   
        } catch (error) {
            console.log(error);
        }
    }

    private parseTile(bundleBase, format, packSize, x, cGroup, y, rGroup, callback) {
        var bundleFileName = bundleBase + ".bundle";
        var context = {
            bundleBase: bundleBase,
            bundleFileName: bundleFileName,
            bundlxFileName: '',
            index: 0,
            storageFormat: format
        };
        if (format === formatV1) {
            context.bundlxFileName = bundleBase + ".bundlx";
            context.index = packSize * (x - cGroup) + (y - rGroup);
        } else if (format === formatV2) {
            console.log('紧凑型2.0版本目前不支持');
            return;
        } else {
            console.log('不支持的格式');
            return;
        }

        try {
            this.readTileFromBundleV1(context, callback);
        } catch (error) {
            console.log(error);
        }
    }

    private readTileFromBundleV1(context, callback) {
        var index = context.index;

        let bundleArrayBuffer = this.cache.get(context.bundleBase).budnleBuffer;
        if(bundleArrayBuffer == null){
            return;
        }
        let dataRW = new DataViewRW(bundleArrayBuffer);

        var sartPosition = 16 + 5 * index;
        var offsetValue = this.getBundleOffset(context, sartPosition); //得到bundle文件中需要跳过的偏移量

        //根据偏移量跳过一些字节
        dataRW.reset();
        dataRW.skip(offsetValue);

        let firstValue = dataRW.getUint8();
        let secondValue = dataRW.getUint8();
        let threeValue = dataRW.getUint8();
        let fourValue = dataRW.getUint8();

        let imageLength = firstValue + secondValue * 2 ** 8 + threeValue * 2 ** 16 + (fourValue << 24);

        dataRW.reset();
        dataRW.skip(offsetValue + 4);
        let imageData = dataRW.getArrayBuffer(imageLength);
      
        // 转换arrybuffer成为base64
        var stringData = String.fromCharCode.apply(null, new Uint16Array(new Uint8Array(imageData)));
        var encodedData = window.btoa(stringData);
        var dataURI = "data:image/jpeg;base64," + encodedData;

        callback(dataURI);
    }

    private getBundleOffset(context, sartPosition: number) {
        let bundleIndexArrayBuffer = this.cache.get(context.bundleBase).indexBuffer;
        let dataRW = new DataViewRW(bundleIndexArrayBuffer);
        //根据偏移量跳过一些字节
        dataRW.reset();
        dataRW.skip(sartPosition);

        let firstValue = dataRW.getUint8();
        let secondValue = dataRW.getUint8();
        let threeValue = dataRW.getUint8();
        let fourValue = dataRW.getUint8();
        let fiveValue = dataRW.getUint8();

        let offsetValue = (fiveValue | (fiveValue & 2 ** 7) * 0x1fffffe) * 2 ** 32 +
            firstValue + secondValue * 2 ** 8 + threeValue * 2 ** 16 + fourValue * 2 ** 24;
        return offsetValue;
    }

    private getBundlePath(root, level, rGroup, cGroup) {
        var bundlesDir = root;
        var l = level.toString();
        var lLength = l.length;
        if (lLength < 2) {
            for (var i = 0; i < 2 - lLength; i++) {
                l = "0" + l;
            }
        }
        l = "L" + l;

        var r = parseInt(rGroup, 10).toString(16);
        var rLength = r.length;
        if (rLength < 4) {
            for (var i = 0; i < 4 - rLength; i++) {
                r = "0" + r;
            }
        }
        r = "R" + r;

        var c = parseInt(cGroup, 10).toString(16);
        var cLength = c.length;
        if (cLength < 4) {
            for (var i = 0; i < 4 - cLength; i++) {
                c = "0" + c;
            }
        }
        c = "C" + c;
        // var bundlePath = bundlesDir + ',_alllayers,' + l + '/' + r + '' + c; //数据放到数据库中的路径拼接方法
        var bundlePath = bundlesDir + '/_alllayers/' + l + '/' + r + '' + c; //数据放到文件夹下的路径拼接方法
        return bundlePath;
    }

    public loadBundleTile(bundleBase: string, parseCallback: Function) {
        this.parseBinaryFile(bundleBase, 'bundlx', parseCallback);

        this.parseBinaryFile(bundleBase, 'bundle', parseCallback);
    }

    private parseBinaryFile(bundleBase: string, type: string, parseCallback) {//解析二进制文件，这里用于切片的bundle和bundlex的解析
        var xhr = new XMLHttpRequest();
        let self = this;
        xhr.onload = function (response: any) {
            if (response.target.status === 404) {
                return;
            }

            var reader = new FileReader();
            reader.onload = function (e) {
                try {
                    let value = self.cache.get(bundleBase);
                    if (type == 'bundlx') {
                        value.indexBuffer = reader.result as ArrayBuffer;
                    } else if (type == 'bundle') {
                        value.budnleBuffer = reader.result as ArrayBuffer;
                    }
    
                    if (value.budnleBuffer != null && value.indexBuffer != null) {
                        parseCallback();
                    }
                } catch (error) {
                    console.log(error);   
                }
               
            }
            reader.readAsArrayBuffer(xhr.response);
        };

        let uri = bundleBase + '.' + type;
        xhr.open('GET', uri, true);
        xhr.responseType = "blob"; // 设置返回类型blob
        xhr.send();
    }

    public clear(){
        if(this.cache){
            this.cache.clear();
            this.cache = null;
        }
    }
}